iT邦幫忙

2023 iThome 鐵人賽

DAY 25
0
Mobile Development

Flutter 從零到實戰 - 30 天の學習筆記系列 第 25

[Day 25] 實戰新聞 APP - 擴充使用者資料 Part 2 (上傳圖片至 firebase storage)

  • 分享至 

  • xImage
  •  

目前為止我們的帳號頁面提供了編輯個人的姓名、信箱與自我介紹的功能。還記得我們昨天定義的 UserModel 嗎?讓我們複習一下:

class UserModel {
  final String name;         // 使用者名字
  final String email;        // 使用者信箱
  final String avatar;       // 使用者頭貼
  final String description;  // 使用者介紹
}

我們還提供了加入使用者頭像圖片網址的欄位,因此今天我們的內容會教大家如何上傳本機的檔案至 firebase 上。

image_picker

套件網址:https://pub.dev/packages/image_picker

昨天有請大家在下載 firestore 套件時也一併下載了此套件。此套件提供於 iOS 與 Android 平台從本機圖片瀏覽庫中挑選圖片或拍照並選擇該相片的功能。

在使用該套件之前我們需要進行些設定。

iOS

請開啟 ios/Runner/Info.plist 檔案,並加入以下設置

<dict>
    <!-- 可能有一大堆 key 或 array 等,加在最下面就好了 -->
    <key>NSPhotoLibraryUsageDescription</key>
	<string>向使用者說明為什麼此應用程式需要使用相片瀏覽器的權限</string>
	<key>NSCameraUsageDescription</key>
	<string>向使用者說明為什麼此應用程式需要使用相機權限</string>
	<key>NSMicrophoneUsageDescription</key>
	<string>向使用者說明為什麼此應用程式需要使用麥克風權限</string>
</dict>

Android

很棒都不需要額外設定 🎉

設定Firebase Storage

Firebase storage 是基於 Google Cloud 基礎架構上,使用於儲存影像、音訊、影片的儲存服務,並為其建立下載 URL。

為什麼我們需要額外使用這項服務?簡單來說現在的相片動轍就 2~3MB 以上,但是我們將用於儲存使用者頭像的欄位 avatarstring 型態,最多僅能容納 1MB 資料。即便經過壓縮後也難以達成此目標。因此我們可以借助 firebase storage 服務來存放使用者所選擇上傳的相片,並產將自動產生的圖片網址儲存於使用者的 avatar 欄位中。

請開啟 Firebase 主控台,並於左側「產品類別」底下的「建構」列表中找到 「Storage」 的項目,成功進入後,請點選「開始使用」來開始建立流程。

步驟1. 首先請選擇,「以測試模式啟動」。
https://ithelp.ithome.com.tw/upload/images/20231010/20135082qI9gtiMrlV.png
步驟2. 設定 Cloud Storage 位置,因為前面 firestore 我們已經設定好的位置,因此這邊也直接套用了相同的位置設定。
https://ithelp.ithome.com.tw/upload/images/20231010/20135082AexyMdAMqC.png

這麼一來就完成拉XDD 設定就是這麼簡單~

挑選圖片

在上傳圖片之前,請先製作一個按鈕來觸發系統跳出選擇圖片的視窗,並參考以下程式碼:

// state 需要多一個變數
Uint8List? image;

Future<Uint8List?> pickImage(ImageSource source) async {
  // ImagePicker() 為挑選圖片的套件提供的方法
    XFile? image = await ImagePicker().pickImage(source: source);
    if (image != null) {
      // 將選擇的圖片內容轉為 Bytes 並回傳
      return await image.readAsBytes();
    }
    // 未挑選圖片則回傳 null
    return null;
  }

void selectImage() async {
  // 指定從相片瀏覽器進行挑選照片
  Uint8List? selected = await pickImage(ImageSource.gallery);
  // 若有挑選圖片
  if (selected != null) {
    setState(() {
      // 將挑選圖片的 bytes 存入 image 中
      image = selected;
      _isChanged = true;
    });
  }
}

// 觸發跳出選擇圖片視窗
onPressed: () {
  selectImage();
}

透過此便可以成功的將圖片資訊存入 image 變數中。此時,若你進行測試時在第一次點擊挑選圖片時,系統會跳出是否允許取用照片權限的通知,請點擊允許,才能成功的取用相片喔!

上傳圖片

既然挑選完圖片了,現在就只差最後一步將使用者的相片上傳拉。請先開啟 UserRepository ,並參考下方程式碼:

class UserRepository {
  final FirebaseFirestore _db = FirebaseFirestore.instance;
  final FirebaseStorage _storage = FirebaseStorage.instance;

  // getUser, createUser, updateUser 省略,請加入下方函式
  // 此函式用於將圖片上傳至 storage,並將產生的圖片網址回傳
  Future<String> uploadImageToStorage(String child, Uint8List file) async {
    Reference ref = _storage.ref().child(child);
    UploadTask uploadTask = ref.putData(file);
    TaskSnapshot snapshot = await uploadTask;
    return await snapshot.ref.getDownloadURL();
  }
}

最後僅需在更新使用者資訊之前,先將檔案上傳並得到圖片網址,再一併將新的使用者資訊與該圖片網址存入更新至 firestore 即可。

// 儲存按鈕,看讀者想放在哪都可以,重要的是流程
CupertinoButton(
  child: const Text('儲存'),
  onPressed: () async {
      try {
        UserModel u = await _user;
        String avatar = u.avatar;
        // 若有選擇圖片才進行上傳檔案的操作
        if (image != null) {
          // 將上傳的檔案名稱命名為使用者 id,如此當使用者上傳新檔案時就會將原檔案蓋過,節省空間
          avatar = await UserRepository().uploadImageToStorage(FirebaseAuth.instance.currentUser!.uid, image!);
        }
        // 更新使用者資訊
        UserRepository().updateUser(
          userId: FirebaseAuth.instance.currentUser!.uid,
          name: _nameTextController.text,   // 名字文字輸入框輸入的值
          description: _descriptionTextController.text, // 介紹文字輸入框輸入的值
          avatar: avatar, // 若有上傳檔案使用新圖片網址,否則使用原本的
        );
      } catch (e) {
        print('發生錯誤: $e')
      }
  }
)

目前為止我們的結果如下圖:
https://ithelp.ithome.com.tw/upload/images/20231010/20135082rx61R9a1TF.png

您也可以隨時至 firestore 的資料頁面檢視您的帳號資料是否有被更新。

接著請開啟 profile_card.dart 的檔案,並將其「使用者名稱」與「自我介紹」的文字顯示部分改為顯示使用者的資訊。這邊就交由各位讀者自行練習~

登出

既然是一個可登入的應用程式,一定也要提供登出的功能!請開啟個人檔案頁面,並於一般設定的 CupertinoListSection.insetGrouped 加入以下程式碼:

CupertinoListSection.insetGrouped(children: [
  // 略過每日一報及夜間模式
  CupertinoListTile.notched(
    title: const Text('登出'),
    leading: DecoratedBox(
        decoration: BoxDecoration(
            borderRadius: BorderRadius.circular(6),
            color: CupertinoColors.systemGrey),
        child: const Padding(
          padding: EdgeInsets.all(4),
          child: Icon(CupertinoIcons.arrow_right_to_line,
              color: CupertinoColors.white, size: 20),
        )),
    onTap: () {
      FirebaseAuth.instance.signOut();
    }),
])

最終結果如下圖,當點擊了登出按鈕後,由於我們先前在 main.dart 中實現了一旦使用者有登入情況導向新聞頁面;未登入導向登入頁面的流程,因此便會直接跳至登入頁面。
https://ithelp.ithome.com.tw/upload/images/20231010/201350824aNAzb4ulL.png

今日總結

往後在開發應用時可能會使用 firebase storage 或其他的檔案系統服務來存儲圖片、影片、音訊甚至是大型檔案等,其實基本上流程無異,都會走類似的流程將檔案上傳至雲端,並產生特定的網址以供下載取用。

在今天的內容中,我們教大家如何將本地圖片上傳至 firebase storage 產生圖片網址,並且將產生的網址連同使用者資訊一同更新到 firestore 中。又得益於 firebase 的整合,使得這兩者之間的串接變得無比容易。

寫到這裡,恭喜各位!我們已經一起將應用程式的所有主要功能皆開發完畢拉!真的是非常不容易 QQ 剩下的篇章,我打算用來添加一些讓應用程式更豐富的功能!

所以終於明天的文章開頭不是「實戰 APP」拉XDD

今天的參考程式碼:https://github.com/ChungHanLin/micro_news_tutorial/tree/day25/micro_news_app


上一篇
[Day 24] 實戰新聞 APP - 擴充使用者資料 Part 1 (設定 Firestore)
下一篇
[Day 26] 進階技巧 - Splash Screen 與 Welcome Screen
系列文
Flutter 從零到實戰 - 30 天の學習筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言